#ifndef __NIGHTXML_INL_H__
#define __NIGHTXML_INL_H__

#include "NightXML.h"


namespace NE
{

	///////////////////////////////////// XmlInternal /////////////////////////////////////

	//! \cond XmlInternal
	namespace XmlInternal
	{

		// Structure that contains lookup tables for the parser
		// It must be a template to allow correct linking (because it has static data members, which are defined in a header file).
		template<int Dummy>
		struct LookupTables
		{
			static const unsigned char lookup_whitespace[256];				// Whitespace table
			static const unsigned char lookup_node_name[256];				// Node name table
			static const unsigned char lookup_text[256];					// Text table
			static const unsigned char lookup_text_pure_no_ws[256];			// Text table
			static const unsigned char lookup_text_pure_with_ws[256];		// Text table
			static const unsigned char lookup_attribute_name[256];			// Attribute name table
			static const unsigned char lookup_attribute_data_1[256];		// Attribute data table with single quote
			static const unsigned char lookup_attribute_data_1_pure[256];	// Attribute data table with single quote
			static const unsigned char lookup_attribute_data_2[256];		// Attribute data table with double quotes
			static const unsigned char lookup_attribute_data_2_pure[256];	// Attribute data table with double quotes
			static const unsigned char lookup_digits[256];					// Digits
			static const unsigned char lookup_upcase[256];					// To uppercase conversion table for ASCII characters
		};

		// Whitespace (space \n \r \t)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_whitespace[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  1,  0,  0,  1,  0,  0,  // 0
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 1
			1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 2
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 3
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 4
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 5
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 6
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 7
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 8
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // 9
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // A
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // B
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // C
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // D
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  // E
			0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0   // F
		};

		// Node name (anything but space \n \r \t / > ? \0)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_node_name[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  1,  1,  0,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Text (i.e. PCDATA) (anything but < \0)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_text[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Text (i.e. PCDATA) that does not require processing when ws normalization is disabled
		// (anything but < \0 &)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_text_pure_no_ws[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Text (i.e. PCDATA) that does not require processing when ws normalizations is enabled
		// (anything but < \0 & space \n \r \t)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_text_pure_with_ws[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  1,  1,  0,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			0,  1,  1,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Attribute name (anything but space \n \r \t / < > = ? ! \0)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_attribute_name[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  1,  1,  0,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			0,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Attribute data with single quote (anything but ' \0)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_attribute_data_1[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Attribute data with single quote that does not require processing (anything but ' \0 &)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_attribute_data_1_pure[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			1,  1,  1,  1,  1,  1,  0,  0,  1,  1,  1,  1,  1,  1,  1,  1,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Attribute data with double quote (anything but " \0)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_attribute_data_2[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Attribute data with double quote that does not require processing (anything but " \0 &)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_attribute_data_2_pure[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 0
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 1
			1,  1,  0,  1,  1,  1,  0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 2
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 3
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 4
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 5
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 6
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 7
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 8
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // 9
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // A
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // B
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // C
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // D
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  // E
			1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1   // F
		};

		// Digits (dec and hex, 255 denotes end of numeric character reference)
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_digits[256] =
		{
			// 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 0
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 1
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 2
			  0,  1,  2,  3,  4,  5,  6,  7,  8,  9,255,255,255,255,255,255,  // 3
			255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255,  // 4
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 5
			255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255,  // 6
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 7
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 8
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // 9
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // A
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // B
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // C
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // D
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,  // E
			255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255   // F
		};

		// Upper case conversion
		template<int Dummy>
		const unsigned char LookupTables<Dummy>::lookup_upcase[256] =
		{
			// 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  A   B   C   D   E   F
			0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14, 15,   // 0
			16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,   // 1
			32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,   // 2
			48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,   // 3
			64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,   // 4
			80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,   // 5
			96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,   // 6
			80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123,124,125,126,127,  // 7
			128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,  // 8
			144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,  // 9
			160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,  // A
			176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,  // B
			192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,  // C
			208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,  // D
			224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,  // E
			240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255   // F
		};

		// Find length of the string
		template<typename tchar>
		inline std::size_t measure(const tchar *p)
		{
			const tchar *tmp = p;
			while (*tmp)
				++tmp;
			return tmp - p;
		}

		// Compare strings for equality
		template<typename tchar>
		inline bool compare(const tchar *p1, std::size_t size1, const tchar *p2, std::size_t size2, bool case_sensitive)
		{
			if (size1 != size2)
				return false;
			if (case_sensitive)
			{
				for (const tchar *end = p1 + size1; p1 < end; ++p1, ++p2)
					if (*p1 != *p2)
						return false;
			}
			else
			{
				for (const tchar *end = p1 + size1; p1 < end; ++p1, ++p2)
					if (LookupTables<0>::lookup_upcase[static_cast<unsigned char>(*p1)] != LookupTables<0>::lookup_upcase[static_cast<unsigned char>(*p2)])
						return false;
			}
			return true;
		}
	}
	//! \endcond


	///////////////////////////////////// XmlMemoryPool /////////////////////////////////////

	template<typename tchar>
	XmlMemoryPool<tchar>::XmlMemoryPool()
		: mAllocFunc(0)
		, mFreeFunc(0)
	{
		init();
	}


	template<typename tchar>
	XmlMemoryPool<tchar>::~XmlMemoryPool()
	{
		clear();
	}


	template<typename tchar>
	XmlNode<tchar>* XmlMemoryPool<tchar>::allocateNode(XmlNodeType type,
		const tchar *name /*= 0*/, const tchar *value /*= 0*/,
		std::size_t name_size /*= 0*/, std::size_t value_size /*= 0*/)
	{
		void *memory = allocateAligned(sizeof(XmlNode<tchar>));
		XmlNode<tchar> *node = new(memory) XmlNode<tchar>(type);
		if (name)
		{
			if (name_size > 0)
				node->setName(name, name_size);
			else
				node->setName(name);
		}
		if (value)
		{
			if (value_size > 0)
				node->setValue(value, value_size);
			else
				node->setValue(value);
		}
		return node;
	}


	template<typename tchar>
	XmlAttribute<tchar>* XmlMemoryPool<tchar>::allocateAttribute(const tchar *name /*= 0*/, const tchar *value /*= 0*/,
		std::size_t name_size /*= 0*/, std::size_t value_size /*= 0*/)
	{
		void *memory = allocateAligned(sizeof(XmlAttribute<tchar>));
		XmlAttribute<tchar> *attribute = new(memory) XmlAttribute<tchar>;
		if (name)
		{
			if (name_size > 0)
				attribute->setName(name, name_size);
			else
				attribute->setName(name);
		}
		if (value)
		{
			if (value_size > 0)
				attribute->setValue(value, value_size);
			else
				attribute->setValue(value);
		}
		return attribute;
	}


	template<typename tchar>
	tchar* XmlMemoryPool<tchar>::allocateString(const tchar *source /*= 0*/, std::size_t size /*= 0*/)
	{
		NightAssertDescription(source || size, "Either source or size (or both) must be specified");	// Either source or size (or both) must be specified
		if (size == 0)
			size = XmlInternal::measure(source) + 1;
		tchar *result = static_cast<tchar *>(allocateAligned(size * sizeof(tchar)));
		if (source)
			for (std::size_t i = 0; i < size; ++i)
				result[i] = source[i];
		return result;
	}


	template<typename tchar>
	XmlNode<tchar>* XmlMemoryPool<tchar>::cloneNode(const XmlNode<tchar> *source, XmlNode<tchar> *result /*= 0*/)
	{
		// Prepare result node
		if (result)
		{
			result->removeAllAttributes();
			result->removeAllNodes();
			result->setType(source->getType());
		}
		else
			result = allocateNode(source->getType());

		// Clone name and value
		result->setName(source->getName(), source->getNameSize());
		result->setValue(source->getValue(), source->getValueSize());

		// Clone child nodes and attributes
		for (XmlNode<tchar> *child = source->getFirstNode(); child; child = child->getNextSibling())
			result->appendNode(cloneNode(child));
		for (XmlAttribute<tchar> *attr = source->getFirstAttribute(); attr; attr = attr->getNextAttribute())
			result->appendAttribute(allocateAttribute(attr->getName(), attr->getValue(), attr->getNameSize(), attr->getValueSize()));

		return result;
	}


	template<typename tchar>
	void XmlMemoryPool<tchar>::clear()
	{
		while (mBegin != mStaticMemory)
		{
			char *previous_begin = reinterpret_cast<header *>(align(mBegin))->previous_begin;
			if (mFreeFunc)
				mFreeFunc(mBegin);
			else
				delete[] mBegin;
			mBegin = previous_begin;
		}
		init();
	}


	template<typename tchar>
	void XmlMemoryPool<tchar>::setAllocator(allocFunc *af, freeFunc *ff)
	{
		NightAssertDescription(mBegin == mStaticMemory && mPtr == align(mBegin), "Verify that no memory is allocated yet");	// Verify that no memory is allocated yet
		mAllocFunc = af;
		mFreeFunc = ff;
	}


	template<typename tchar>
	void XmlMemoryPool<tchar>::init()
	{
		mBegin = mStaticMemory;
		mPtr = align(mBegin);
		mEnd = mStaticMemory + sizeof(mStaticMemory);
	}


	template<typename tchar>
	char* XmlMemoryPool<tchar>::align(char *ptr)
	{
		std::size_t alignment = ((XML_ALIGNMENT - (std::size_t(ptr) & (XML_ALIGNMENT - 1))) & (XML_ALIGNMENT - 1));
		return ptr + alignment;
	}


	template<typename tchar>
	char* XmlMemoryPool<tchar>::allocateRaw(std::size_t size)
	{
		// Allocate
		void *memory;
		if (mAllocFunc)		// Allocate memory using either user-specified allocation function or global operator new[]
		{
			memory = mAllocFunc(size);
			// Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp
			NightAssertDescription(memory, "Allocator is not allowed to return 0, on failure it must either throw, stop the program or use longjmp");
		}
		else
		{
			memory = new char[size];
			if (!memory)
				NightExcept(ERR_PARSE_XML, "out of memory", 0);
		}
		return static_cast<char *>(memory);
	}


	template<typename tchar>
	void* XmlMemoryPool<tchar>::allocateAligned(std::size_t size)
	{
		// Calculate aligned pointer
		char *result = align(mPtr);

		// If not enough memory left in current pool, allocate a new pool
		if (result + size > mEnd)
		{
			// Calculate required pool size (may be bigger than XML_DYNAMIC_POOL_SIZE)
			std::size_t pool_size = XML_DYNAMIC_POOL_SIZE;
			if (pool_size < size)
				pool_size = size;

			// Allocate
			std::size_t alloc_size = sizeof(header) + (2 * XML_ALIGNMENT - 2) + pool_size;		// 2 alignments required in worst case: one for header, one for actual allocation
			char *raw_memory = allocateRaw(alloc_size);

			// Setup new pool in allocated memory
			char *pool = align(raw_memory);
			header *new_header = reinterpret_cast<header *>(pool);
			new_header->previous_begin = mBegin;
			mBegin = raw_memory;
			mPtr = pool + sizeof(header);
			mEnd = raw_memory + alloc_size;

			// Calculate aligned pointer again using new pool
			result = align(mPtr);
		}

		// Update pool and return aligned pointer
		mPtr = result + size;
		return result;
	}


	///////////////////////////////////// XmlBase /////////////////////////////////////

	template<typename tchar>
	XmlBase<tchar>::XmlBase()
	: mName(0)
	, mValue(0)
	, mNameSize(0)
	, mValueSize(0)
	, mParent(0)
	{
		//
	}


	template<typename tchar>
	tchar* XmlBase<tchar>::getName() const
	{
		return mName ? mName : nullstr();
	}


	template<typename tchar>
	std::size_t XmlBase<tchar>::getNameSize() const
	{
		return mName ? mNameSize : 0;
	}


	template<typename tchar>
	tchar* XmlBase<tchar>::getValue() const
	{
		return mValue ? mValue : nullstr();
	}


	template<typename tchar>
	std::size_t XmlBase<tchar>::getValueSize() const
	{
		return mValue ? mValueSize : 0;
	}


	template<typename tchar>
	void XmlBase<tchar>::setName(const tchar *name, std::size_t size)
	{
		mName = const_cast<tchar *>(name);
		mNameSize = size;
	}


	template<typename tchar>
	void XmlBase<tchar>::setName(const tchar *name)
	{
		this->setName(name, XmlInternal::measure(name));
	}


	template<typename tchar>
	void XmlBase<tchar>::setValue(const tchar *value, std::size_t size)
	{
		mValue = const_cast<tchar *>(value);
		mValueSize = size;
	}


	template<typename tchar>
	void XmlBase<tchar>::setValue(const tchar *value)
	{
		this->setValue(value, XmlInternal::measure(value));
	}


	template<typename tchar>
	XmlNode<tchar>* XmlBase<tchar>::getParent() const
	{
		return mParent;
	}


	template<typename tchar>
	tchar* XmlBase<tchar>::nullstr()
	{
		static tchar zero = tchar('\0');
		return &zero;
	}


	///////////////////////////////////// XmlAttribute /////////////////////////////////////


	template<typename tchar>
	XmlAttribute<tchar>::XmlAttribute()
		: mPrevAttribute(0)
		, mNextAttribute(0)
	{
		//
	}


	template<typename tchar>
	XmlDocument<tchar>* XmlAttribute<tchar>::getDocument() const
	{
		if (XmlNode<tchar> *node = this->getParent())
		{
			while (node->getParent())
				node = node->getParent();
			return node->getType() == XML_NODE_DOCUMENT ? static_cast<XmlDocument<tchar> *>(node) : 0;
		}
		else
			return 0;
	}


	template<typename tchar>
	XmlAttribute<tchar>* XmlAttribute<tchar>::getPreviousAttribute(const tchar *name /*= 0*/, std::size_t name_size /*= 0*/, bool case_sensitive /*= true*/) const
	{
		if (name)
		{
			if (name_size == 0)
				name_size = XmlInternal::measure(name);
			for (XmlAttribute<tchar> *attribute = mPrevAttribute; attribute; attribute = attribute->mPrevAttribute)
				if (XmlInternal::compare(attribute->getName(), attribute->getNameSize(), name, name_size, case_sensitive))
					return attribute;
			return 0;
		}
		else
			return this->mParent ? mPrevAttribute : 0;
	}


	template<typename tchar>
	XmlAttribute<tchar>* XmlAttribute<tchar>::getNextAttribute(const tchar *name /*= 0*/, std::size_t name_size /*= 0*/, bool case_sensitive /*= true*/) const
	{
		if (name)
		{
			if (name_size == 0)
				name_size = XmlInternal::measure(name);
			for (XmlAttribute<tchar> *attribute = mNextAttribute; attribute; attribute = attribute->mNextAttribute)
				if (XmlInternal::compare(attribute->getName(), attribute->getNameSize(), name, name_size, case_sensitive))
					return attribute;
			return 0;
		}
		else
			return this->mParent ? mNextAttribute : 0;
	}


	///////////////////////////////////// XmlNode /////////////////////////////////////


	template<typename tchar>
	XmlNode<tchar>::XmlNode(XmlNodeType type)
		: mType(type)
		, mFirstNode(0)
		, mLastNode(0)
		, mFirstAttribute(0)
		, mLastAttribute(0)
		, mPrevSibling(0)
		, mNextSibling(0)
	{
		//
	}


	template<typename tchar>
	XmlNodeType XmlNode<tchar>::getType() const
	{
		return mType;
	}


	template<typename tchar>
	XmlDocument<tchar>* XmlNode<tchar>::getDocument() const
	{
		XmlNode<tchar> *node = const_cast<XmlNode<tchar> *>(this);
		while (node->getParent())
			node = node->getParent();
		return node->getType() == XML_NODE_DOCUMENT ? static_cast<XmlDocument<tchar> *>(node) : 0;
	}


	template<typename tchar>
	XmlNode<tchar>* XmlNode<tchar>::getFirstNode(const tchar *name /*= 0*/, std::size_t name_size /*= 0*/, bool case_sensitive /*= true*/) const
	{
		if (name)
		{
			if (name_size == 0)
				name_size = XmlInternal::measure(name);
			for (XmlNode<tchar> *child = mFirstNode; child; child = child->getNextSibling())
				if (XmlInternal::compare(child->getName(), child->getNameSize(), name, name_size, case_sensitive))
					return child;
			return 0;
		}
		else
			return mFirstNode;
	}


	template<typename tchar>
	XmlNode<tchar>* XmlNode<tchar>::getLastNode(const tchar *name /*= 0*/, std::size_t name_size /*= 0*/, bool case_sensitive /*= true*/) const
	{
		NightAssertDescription(mFirstNode, "Cannot query for last child if node has no children");		// Cannot query for last child if node has no children
		if (name)
		{
			if (name_size == 0)
				name_size = XmlInternal::measure(name);
			for (XmlNode<tchar> *child = mLastNode; child; child = child->getPreviousSibling())
				if (XmlInternal::compare(child->getName(), child->getNameSize(), name, name_size, case_sensitive))
					return child;
			return 0;
		}
		else
			return mLastNode;
	}


	template<typename tchar>
	XmlNode<tchar>* XmlNode<tchar>::getPreviousSibling(const tchar *name /*= 0*/, std::size_t name_size /*= 0*/, bool case_sensitive /*= true*/) const
	{
		NightAssertDescription(this->mParent, "Cannot query for siblings if node has no parent");		// Cannot query for siblings if node has no parent
		if (name)
		{
			if (name_size == 0)
				name_size = XmlInternal::measure(name);
			for (XmlNode<tchar> *sibling = mPrevSibling; sibling; sibling = sibling->mPrevSibling)
				if (XmlInternal::compare(sibling->getName(), sibling->getNameSize(), name, name_size, case_sensitive))
					return sibling;
			return 0;
		}
		else
			return mPrevSibling;
	}


	template<typename tchar>
	XmlNode<tchar>* XmlNode<tchar>::getNextSibling(const tchar *name /*= 0*/, std::size_t name_size /*= 0*/, bool case_sensitive /*= true*/) const
	{
		NightAssertDescription(this->mParent, "Cannot query for siblings if node has no parent");		// Cannot query for siblings if node has no parent
		if (name)
		{
			if (name_size == 0)
				name_size = XmlInternal::measure(name);
			for (XmlNode<tchar> *sibling = mNextSibling; sibling; sibling = sibling->mNextSibling)
				if (XmlInternal::compare(sibling->getName(), sibling->getNameSize(), name, name_size, case_sensitive))
					return sibling;
			return 0;
		}
		else
			return mNextSibling;
	}


	template<typename tchar>
	XmlAttribute<tchar>* XmlNode<tchar>::getFirstAttribute(const tchar *name /*= 0*/, std::size_t name_size /*= 0*/, bool case_sensitive /*= true*/) const
	{
		if (name)
		{
			if (name_size == 0)
				name_size = XmlInternal::measure(name);
			for (XmlAttribute<tchar> *attribute = mFirstAttribute; attribute; attribute = attribute->mNextAttribute)
				if (XmlInternal::compare(attribute->getName(), attribute->getNameSize(), name, name_size, case_sensitive))
					return attribute;
			return 0;
		}
		else
			return mFirstAttribute;
	}


	template<typename tchar>
	XmlAttribute<tchar>* XmlNode<tchar>::getLastAttribute(const tchar *name /*= 0*/, std::size_t name_size /*= 0*/, bool case_sensitive /*= true*/) const
	{
		if (name)
		{
			if (name_size == 0)
				name_size = XmlInternal::measure(name);
			for (XmlAttribute<tchar> *attribute = mLastAttribute; attribute; attribute = attribute->mPrevAttribute)
				if (XmlInternal::compare(attribute->getName(), attribute->getNameSize(), name, name_size, case_sensitive))
					return attribute;
			return 0;
		}
		else
			return mFirstAttribute ? mLastAttribute : 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::setType(XmlNodeType type)
	{
		mType = type;
	}


	template<typename tchar>
	void XmlNode<tchar>::prependNode(XmlNode<tchar> *child)
	{
		NightAssert(child && !child->getParent() && child->getType() != XML_NODE_DOCUMENT);
		if (getFirstNode())
		{
			child->mNextSibling = mFirstNode;
			mFirstNode->mPrevSibling = child;
		}
		else
		{
			child->mNextSibling = 0;
			mLastNode = child;
		}
		mFirstNode = child;
		child->mParent = this;
		child->mPrevSibling = 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::appendNode(XmlNode<tchar> *child)
	{
		NightAssert(child && !child->getParent() && child->getType() != XML_NODE_DOCUMENT);
		if (getFirstNode())
		{
			child->mPrevSibling = mLastNode;
			mLastNode->mNextSibling = child;
		}
		else
		{
			child->mPrevSibling = 0;
			mFirstNode = child;
		}
		mLastNode = child;
		child->mParent = this;
		child->mNextSibling = 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::insertNode(XmlNode<tchar> *where, XmlNode<tchar> *child)
	{
		NightAssert(!where || where->getParent() == this);
		NightAssert(child && !child->getParent() && child->getType() != XML_NODE_DOCUMENT);
		if (where == mFirstNode)
			prependNode(child);
		else if (where == 0)
			appendNode(child);
		else
		{
			child->mPrevSibling = where->mPrevSibling;
			child->mNextSibling = where;
			where->mPrevSibling->mNextSibling = child;
			where->mPrevSibling = child;
			child->mParent = this;
		}
	}


	template<typename tchar>
	void XmlNode<tchar>::removeFirstNode()
	{
		NightAssert(getFirstNode());
		XmlNode<tchar> *child = mFirstNode;
		mFirstNode = child->mNextSibling;
		if (child->mNextSibling)
			child->mNextSibling->mPrevSibling = 0;
		else
			mLastNode = 0;
		child->mParent = 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::removeLastNode()
	{
		NightAssert(getFirstNode());
		XmlNode<tchar> *child = mLastNode;
		if (child->mPrevSibling)
		{
			mLastNode = child->mPrevSibling;
			child->mPrevSibling->mNextSibling = 0;
		}
		else
			mFirstNode = 0;
		child->mParent = 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::removeNode(XmlNode<tchar> *where)
	{
		NightAssert(where && where->getParent() == this);
		NightAssert(getFirstNode());
		if (where == mFirstNode)
			removeFirstNode();
		else if (where == mLastNode)
			removeLastNode();
		else
		{
			where->mPrevSibling->mNextSibling = where->mNextSibling;
			where->mNextSibling->mPrevSibling = where->mPrevSibling;
			where->mParent = 0;
		}
	}


	template<typename tchar>
	void XmlNode<tchar>::removeAllNodes()
	{
		for (XmlNode<tchar> *node = getFirstNode(); node; node = node->mNextSibling)
			node->mParent = 0;
		mFirstNode = 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::prependAttribute(XmlAttribute<tchar> *attribute)
	{
		NightAssert(attribute && !attribute->getParent());
		if (getFirstAttribute())
		{
			attribute->mNextAttribute = mFirstAttribute;
			mFirstAttribute->mPrevAttribute = attribute;
		}
		else
		{
			attribute->mNextAttribute = 0;
			mLastAttribute = attribute;
		}
		mFirstAttribute = attribute;
		attribute->mParent = this;
		attribute->mPrevAttribute = 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::appendAttribute(XmlAttribute<tchar> *attribute)
	{
		NightAssert(attribute && !attribute->getParent());
		if (getFirstAttribute())
		{
			attribute->mPrevAttribute = mLastAttribute;
			mLastAttribute->mNextAttribute = attribute;
		}
		else
		{
			attribute->mPrevAttribute = 0;
			mFirstAttribute = attribute;
		}
		mLastAttribute = attribute;
		attribute->mParent = this;
		attribute->mNextAttribute = 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::insertAttribute(XmlAttribute<tchar> *where, XmlAttribute<tchar> *attribute)
	{
		NightAssert(!where || where->getParent() == this);
		NightAssert(attribute && !attribute->getParent());
		if (where == mFirstAttribute)
			prependAttribute(attribute);
		else if (where == 0)
			appendAttribute(attribute);
		else
		{
			attribute->mPrevAttribute = where->mPrevAttribute;
			attribute->mNextAttribute = where;
			where->mPrevAttribute->mNextAttribute = attribute;
			where->mPrevAttribute = attribute;
			attribute->mParent = this;
		}
	}


	template<typename tchar>
	void XmlNode<tchar>::removeFirstAttribute()
	{
		NightAssert(getFirstAttribute());
		XmlAttribute<tchar> *attribute = mFirstAttribute;
		if (attribute->mNextAttribute)
		{
			attribute->mNextAttribute->mPrevAttribute = 0;
		}
		else
			mLastAttribute = 0;
		attribute->mParent = 0;
		mFirstAttribute = attribute->mNextAttribute;
	}


	template<typename tchar>
	void XmlNode<tchar>::removeLastAttribute()
	{
		NightAssert(getFirstAttribute());
		XmlAttribute<tchar> *attribute = mLastAttribute;
		if (attribute->mPrevAttribute)
		{
			attribute->mPrevAttribute->mNextAttribute = 0;
			mLastAttribute = attribute->mPrevAttribute;
		}
		else
			mFirstAttribute = 0;
		attribute->mParent = 0;
	}


	template<typename tchar>
	void XmlNode<tchar>::removeAttribute(XmlAttribute<tchar> *where)
	{
		NightAssert(getFirstAttribute() && where->getParent() == this);
		if (where == mFirstAttribute)
			removeFirstAttribute();
		else if (where == mLastAttribute)
			removeLastAttribute();
		else
		{
			where->mPrevAttribute->mNextAttribute = where->mNextAttribute;
			where->mNextAttribute->mPrevAttribute = where->mPrevAttribute;
			where->mParent = 0;
		}
	}


	template<typename tchar>
	void XmlNode<tchar>::removeAllAttributes()
	{
		for (XmlAttribute<tchar> *attribute = getFirstAttribute(); attribute; attribute = attribute->mNextAttribute)
			attribute->mParent = 0;
		mFirstAttribute = 0;
	}


	///////////////////////////////////// XmlDocument /////////////////////////////////////


	template<typename tchar>
	XmlDocument<tchar>::XmlDocument()
		: XmlNode<tchar>(XML_NODE_DOCUMENT)
	{
		//
	}


	template<typename tchar>
	template<int Flags>
	void XmlDocument<tchar>::parse(tchar *text)
	{
		NightAssertDescription(text, "no data to parse!");

		// Remove current contents
		this->removeAllNodes();
		this->removeAllAttributes();

		// Parse BOM, if any
		parseBom<Flags>(text);

		// Parse children
		while (true)
		{
			// Skip whitespace before node
			skip<WhitespacePred, Flags>(text);
			if (*text == 0)
				break;

			// Parse and append new child
			if (*text == tchar('<'))
			{
				++text;		// Skip '<'
				if (XmlNode<tchar> *node = parseNode<Flags>(text))
					this->appendNode(node);
			}
			else
				NightExcept(ERR_PARSE_XML, "expected <", text);
		}
	}


	template<typename tchar>
	template<int Flags>
	void XmlDocument<tchar>::parse(const tchar *text)
	{
		this->parse<Flags | xml_parse_non_destructive>(const_cast<tchar *>(text));
	}


	template<typename tchar>
	void XmlDocument<tchar>::clear()
	{
		this->removeAllNodes();
		this->removeAllAttributes();
		XmlMemoryPool<tchar>::clear();
	}


	template<typename tchar>
	unsigned char XmlDocument<tchar>::WhitespacePred::test(tchar ch)
	{
		return XmlInternal::LookupTables<0>::lookup_whitespace[static_cast<unsigned char>(ch)];
	}


	template<typename tchar>
	unsigned char XmlDocument<tchar>::NodeNamePred::test(tchar ch)
	{
		return XmlInternal::LookupTables<0>::lookup_node_name[static_cast<unsigned char>(ch)];
	}


	template<typename tchar>
	unsigned char XmlDocument<tchar>::AttributeNamePred::test(tchar ch)
	{
		return XmlInternal::LookupTables<0>::lookup_attribute_name[static_cast<unsigned char>(ch)];
	}


	template<typename tchar>
	unsigned char XmlDocument<tchar>::TextPred::test(tchar ch)
	{
		return XmlInternal::LookupTables<0>::lookup_text[static_cast<unsigned char>(ch)];
	}


	template<typename tchar>
	unsigned char XmlDocument<tchar>::TextPureNoWsPred::test(tchar ch)
	{
		return XmlInternal::LookupTables<0>::lookup_text_pure_no_ws[static_cast<unsigned char>(ch)];
	}


	template<typename tchar>
	unsigned char XmlDocument<tchar>::TextPureWithWsPred::test(tchar ch)
	{
		return XmlInternal::LookupTables<0>::lookup_text_pure_with_ws[static_cast<unsigned char>(ch)];
	}


	template<typename tchar>
	template<tchar Quote>
	unsigned char XmlDocument<tchar>::AttributeValuePred<Quote>::test(tchar ch)
	{
		if (Quote == tchar('\''))
			return XmlInternal::LookupTables<0>::lookup_attribute_data_1[static_cast<unsigned char>(ch)];
		if (Quote == tchar('\"'))
			return XmlInternal::LookupTables<0>::lookup_attribute_data_2[static_cast<unsigned char>(ch)];
		return 0;	// Should never be executed, to avoid warnings on Comeau
	}


	template<typename tchar>
	template<tchar Quote>
	unsigned char XmlDocument<tchar>::AttributeValuePurePred<Quote>::test(tchar ch)
	{
		if (Quote == tchar('\''))
			return XmlInternal::LookupTables<0>::lookup_attribute_data_1_pure[static_cast<unsigned char>(ch)];
		if (Quote == tchar('\"'))
			return XmlInternal::LookupTables<0>::lookup_attribute_data_2_pure[static_cast<unsigned char>(ch)];
		return 0;	// Should never be executed, to avoid warnings on Comeau
	}


	template<typename tchar>
	template<int Flags>
	void XmlDocument<tchar>::insertCodedCharacter(tchar *&text, unsigned long code)
	{
		if (Flags & xml_parse_no_utf8)
		{
			// Insert 8-bit ASCII character
			// TODO: possibly verify that code is less than 256 and use replacement char otherwise?
			text[0] = static_cast<unsigned char>(code);
			text += 1;
		}
		else
		{
			// Insert UTF8 sequence
			if (code < 0x80)	// 1 byte sequence
			{
				text[0] = static_cast<unsigned char>(code);
				text += 1;
			}
			else if (code < 0x800)	// 2 byte sequence
			{
				text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
				text[0] = static_cast<unsigned char>(code | 0xC0);
				text += 2;
			}
			else if (code < 0x10000)	// 3 byte sequence
			{
				text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
				text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
				text[0] = static_cast<unsigned char>(code | 0xE0);
				text += 3;
			}
			else if (code < 0x110000)	// 4 byte sequence
			{
				text[3] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
				text[2] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
				text[1] = static_cast<unsigned char>((code | 0x80) & 0xBF); code >>= 6;
				text[0] = static_cast<unsigned char>(code | 0xF0);
				text += 4;
			}
			else	// Invalid, only codes up to 0x10FFFF are allowed in Unicode
			{
				NightExcept(ERR_PARSE_XML, "invalid numeric character entity", 0);
			}
		}
	}


	template<typename tchar>
	template<typename StopPred, int Flags>
	void XmlDocument<tchar>::skip(tchar *&text)
	{
		tchar *tmp = text;
		while (StopPred::test(*tmp))
			++tmp;
		text = tmp;
	}


	template<typename tchar>
	template<typename StopPred, typename StopPredPure, int Flags>
	tchar* XmlDocument<tchar>::skipAndExpandCharacterRefs(tchar *&text)
	{
		// If entity translation, whitespace condense and whitespace trimming is disabled, use plain skip
		if (Flags & xml_parse_no_entity_translation &&
			!(Flags & xml_parse_normalize_whitespace) &&
			!(Flags & xml_parse_trim_whitespace))
		{
			skip<StopPred, Flags>(text);
			return text;
		}

		// Use simple skip until first modification is detected
		skip<StopPredPure, Flags>(text);

		// Use translation skip
		tchar *src = text;
		tchar *dest = src;
		while (StopPred::test(*src))
		{
			// If entity translation is enabled
			if (!(Flags & xml_parse_no_entity_translation))
			{
				// Test if replacement is needed
				if (src[0] == tchar('&'))
				{
					switch (src[1])
					{
						// &amp; &apos;
					case tchar('a'):
						if (src[2] == tchar('m') && src[3] == tchar('p') && src[4] == tchar(';'))
						{
							*dest = tchar('&');
							++dest;
							src += 5;
							continue;
						}
						if (src[2] == tchar('p') && src[3] == tchar('o') && src[4] == tchar('s') && src[5] == tchar(';'))
						{
							*dest = tchar('\'');
							++dest;
							src += 6;
							continue;
						}
						break;

						// &quot;
					case tchar('q'):
						if (src[2] == tchar('u') && src[3] == tchar('o') && src[4] == tchar('t') && src[5] == tchar(';'))
						{
							*dest = tchar('"');
							++dest;
							src += 6;
							continue;
						}
						break;

						// &gt;
					case tchar('g'):
						if (src[2] == tchar('t') && src[3] == tchar(';'))
						{
							*dest = tchar('>');
							++dest;
							src += 4;
							continue;
						}
						break;

						// &lt;
					case tchar('l'):
						if (src[2] == tchar('t') && src[3] == tchar(';'))
						{
							*dest = tchar('<');
							++dest;
							src += 4;
							continue;
						}
						break;

						// &#...; - assumes ASCII
					case tchar('#'):
						if (src[2] == tchar('x'))
						{
							unsigned long code = 0;
							src += 3;		// Skip &#x
							while (true)
							{
								unsigned char digit = XmlInternal::LookupTables<0>::lookup_digits[static_cast<unsigned char>(*src)];
								if (digit == 0xFF)
									break;
								code = code * 16 + digit;
								++src;
							}
							insertCodedCharacter<Flags>(dest, code);		// Put character in output
						}
						else
						{
							unsigned long code = 0;
							src += 2;		// Skip &#
							while (true)
							{
								unsigned char digit = XmlInternal::LookupTables<0>::lookup_digits[static_cast<unsigned char>(*src)];
								if (digit == 0xFF)
									break;
								code = code * 10 + digit;
								++src;
							}
							insertCodedCharacter<Flags>(dest, code);		// Put character in output
						}
						if (*src == tchar(';'))
							++src;
						else
							NightExcept(ERR_PARSE_XML, "expected ;", src);
						continue;

						// Something else
					default:
						// Ignore, just copy '&' verbatim
						break;
					}
				}
			}

			// If whitespace condensing is enabled
			if (Flags & xml_parse_normalize_whitespace)
			{
				// Test if condensing is needed
				if (WhitespacePred::test(*src))
				{
					*dest = tchar(' ');
					++dest;		// Put single space in dest
					++src;		// Skip first whitespace char
					// Skip remaining whitespace chars
					while (WhitespacePred::test(*src))
						++src;
					continue;
				}
			}

			// No replacement, only copy character
			*dest++ = *src++;
		}

		// Return new end
		text = src;
		return dest;
	}


	template<typename tchar>
	template<int Flags>
	void XmlDocument<tchar>::parseBom(tchar *&text)
	{
		unsigned char*& _text = reinterpret_cast<unsigned char*&>(text);

		// Little-Endian?
		if (_text[0] == 0xFF && _text[1] == 0xFE)
		{
			_text += 2;
		}

		// Big-Endian?
		else if (_text[0] == 0xFE && _text[1] == 0xFF)
		{
			_text += 2;
		}

		// UTF-8?
		else if (_text[0] == 0xEF && _text[1] == 0xBB && _text[2] == 0xBF)
		{
			_text += 3;		// Skip UTF-8 BOM
		}
	}


	template<typename tchar>
	template<int Flags>
	XmlNode<tchar>* XmlDocument<tchar>::parseXmlDeclaration(tchar *&text)
	{
		// If parsing of declaration is disabled
		if (!(Flags & xml_parse_declaration_node))
		{
			// Skip until end of declaration
			while (text[0] != tchar('?') || text[1] != tchar('>'))
			{
				if (!text[0])
					NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
				++text;
			}
			text += 2;		// Skip '?>'
			return 0;
		}

		// Create declaration
		XmlNode<tchar> *declaration = this->allocateNode(XML_NODE_DECLARATION);

		// Skip whitespace before attributes or ?>
		skip<WhitespacePred, Flags>(text);

		// Parse declaration attributes
		parseNodeAttributes<Flags>(text, declaration);

		// Skip ?>
		if (text[0] != tchar('?') || text[1] != tchar('>'))
			NightExcept(ERR_PARSE_XML, "expected ?>", text);
		text += 2;

		return declaration;
	}


	template<typename tchar>
	template<int Flags>
	XmlNode<tchar>* XmlDocument<tchar>::parseComment(tchar *&text)
	{
		// If parsing of comments is disabled
		if (!(Flags & xml_parse_comment_nodes))
		{
			// Skip until end of comment
			while (text[0] != tchar('-') || text[1] != tchar('-') || text[2] != tchar('>'))
			{
				if (!text[0])
					NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
				++text;
			}
			text += 3;		// Skip '-->'
			return 0;		// Do not produce comment node
		}

		// Remember value start
		tchar *value = text;

		// Skip until end of comment
		while (text[0] != tchar('-') || text[1] != tchar('-') || text[2] != tchar('>'))
		{
			if (!text[0])
				NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
			++text;
		}

		// Create comment node
		XmlNode<tchar> *comment = this->allocateNode(XML_NODE_COMMENT);
		comment->setValue(value, text - value);

		// Place zero terminator after comment value
		if (!(Flags & xml_parse_no_string_terminators))
			*text = tchar('\0');

		text += 3;		// Skip '-->'
		return comment;
	}


	template<typename tchar>
	template<int Flags>
	XmlNode<tchar>* XmlDocument<tchar>::parseDoctype(tchar *&text)
	{
		// Remember value start
		tchar *value = text;

		// Skip to >
		while (*text != tchar('>'))
		{
			// Determine character type
			switch (*text)
			{

				// If '[' encountered, scan for matching ending ']' using naive algorithm with depth
				// This works for all W3C test files except for 2 most wicked
			case tchar('['):
				{
					++text;		// Skip '['
					int depth = 1;
					while (depth > 0)
					{
						switch (*text)
						{
						case tchar('['): ++depth; break;
						case tchar(']'): --depth; break;
						case 0: NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
						}
						++text;
					}
					break;
				}

				// Error on end of text
			case tchar('\0'):
				NightExcept(ERR_PARSE_XML, "unexpected end of data", text);

				// Other character, skip it
			default:
				++text;
			}
		}

		// If DOCTYPE nodes enabled
		if (Flags & xml_parse_doctype_node)
		{
			// Create a new doctype node
			XmlNode<tchar> *doctype = this->allocateNode(XML_NODE_DOCTYPE);
			doctype->setValue(value, text - value);

			// Place zero terminator after value
			if (!(Flags & xml_parse_no_string_terminators))
				*text = tchar('\0');

			text += 1;		// skip '>'
			return doctype;
		}
		else
		{
			text += 1;		// skip '>'
			return 0;
		}
	}


	template<typename tchar>
	template<int Flags>
	XmlNode<tchar>* XmlDocument<tchar>::parsePI(tchar *&text)
	{
		// If creation of PI nodes is enabled
		if (Flags & xml_parse_pi_nodes)
		{
			// Create pi node
			XmlNode<tchar> *pi = this->allocateNode(XML_NODE_PI);

			// Extract PI target name
			tchar *name = text;
			skip<NodeNamePred, Flags>(text);
			if (text == name)
				NightExcept(ERR_PARSE_XML, "expected PI target", text);
			pi->setName(name, text - name);

			// Skip whitespace between pi target and pi
			skip<WhitespacePred, Flags>(text);

			// Remember start of pi
			tchar *value = text;

			// Skip to '?>'
			while (text[0] != tchar('?') || text[1] != tchar('>'))
			{
				if (*text == tchar('\0'))
					NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
				++text;
			}

			// Set pi value (verbatim, no entity expansion or whitespace normalization)
			pi->setValue(value, text - value);

			// Place zero terminator after name and value
			if (!(Flags & xml_parse_no_string_terminators))
			{
				pi->getName()[pi->getNameSize()] = tchar('\0');
				pi->getValue()[pi->getValueSize()] = tchar('\0');
			}

			text += 2;		// Skip '?>'
			return pi;
		}
		else
		{
			// Skip to '?>'
			while (text[0] != tchar('?') || text[1] != tchar('>'))
			{
				if (*text == tchar('\0'))
					NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
				++text;
			}
			text += 2;		// Skip '?>'
			return 0;
		}
	}


	template<typename tchar>
	template<int Flags>
	tchar XmlDocument<tchar>::parseAndAppendData(XmlNode<tchar> *node, tchar *&text, tchar *contents_start)
	{
		// Backup to contents start if whitespace trimming is disabled
		if (!(Flags & xml_parse_trim_whitespace))
			text = contents_start;

		// Skip until end of data
		tchar *value = text, *end;
		if (Flags & xml_parse_normalize_whitespace)
			end = skipAndExpandCharacterRefs<TextPred, TextPureWithWsPred, Flags>(text);
		else
			end = skipAndExpandCharacterRefs<TextPred, TextPureNoWsPred, Flags>(text);

		// Trim trailing whitespace if flag is set; leading was already trimmed by whitespace skip after >
		if (Flags & xml_parse_trim_whitespace)
		{
			if (Flags & xml_parse_normalize_whitespace)
			{
				// Whitespace is already condensed to single space characters by skipping function, so just trim 1 char off the end
				if (*(end - 1) == tchar(' '))
					--end;
			}
			else
			{
				// Backup until non-whitespace character is found
				while (WhitespacePred::test(*(end - 1)))
					--end;
			}
		}

		// If characters are still left between end and value (this test is only necessary if normalization is enabled)
		// Create new data node
		if (!(Flags & xml_parse_no_data_nodes))
		{
			XmlNode<tchar> *data = this->allocateNode(XML_NODE_DATA);
			data->setValue(value, end - value);
			node->appendNode(data);
		}

		// Add data to parent node if no data exists yet
		if (!(Flags & xml_parse_no_element_values))
			if (*node->getValue() == tchar('\0'))
				node->setValue(value, end - value);

		// Place zero terminator after value
		if (!(Flags & xml_parse_no_string_terminators))
		{
			tchar ch = *text;
			*end = tchar('\0');
			return ch;		// Return character that ends data; this is required because zero terminator overwritten it
		}

		// Return character that ends data
		return *text;
	}


	template<typename tchar>
	template<int Flags>
	XmlNode<tchar>* XmlDocument<tchar>::parseCdata(tchar *&text)
	{
		// If CDATA is disabled
		if (Flags & xml_parse_no_data_nodes)
		{
			// Skip until end of cdata
			while (text[0] != tchar(']') || text[1] != tchar(']') || text[2] != tchar('>'))
			{
				if (!text[0])
					NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
				++text;
			}
			text += 3;		// Skip ]]>
			return 0;		// Do not produce CDATA node
		}

		// Skip until end of cdata
		tchar *value = text;
		while (text[0] != tchar(']') || text[1] != tchar(']') || text[2] != tchar('>'))
		{
			if (!text[0])
				NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
			++text;
		}

		// Create new cdata node
		XmlNode<tchar> *cdata = this->allocateNode(XML_NODE_CDATA);
		cdata->setValue(value, text - value);

		// Place zero terminator after value
		if (!(Flags & xml_parse_no_string_terminators))
			*text = tchar('\0');

		text += 3;		// Skip ]]>
		return cdata;
	}


	template<typename tchar>
	template<int Flags>
	XmlNode<tchar>* XmlDocument<tchar>::parseElement(tchar *&text)
	{
		// Create element node
		XmlNode<tchar> *element = this->allocateNode(XML_NODE_ELEMENT);

		// Extract element name
		tchar *name = text;
		skip<NodeNamePred, Flags>(text);
		if (text == name)
			NightExcept(ERR_PARSE_XML, "expected element name", text);
		element->setName(name, text - name);

		// Skip whitespace between element name and attributes or >
		skip<WhitespacePred, Flags>(text);

		// Parse attributes, if any
		parseNodeAttributes<Flags>(text, element);

		// Determine ending type
		if (*text == tchar('>'))
		{
			++text;
			parseNodeContents<Flags>(text, element);
		}
		else if (*text == tchar('/'))
		{
			++text;
			if (*text != tchar('>'))
				NightExcept(ERR_PARSE_XML, "expected >", text);
			++text;
		}
		else
			NightExcept(ERR_PARSE_XML, "expected >", text);

		// Place zero terminator after name
		if (!(Flags & xml_parse_no_string_terminators))
			element->getName()[element->getNameSize()] = tchar('\0');

		// Return parsed element
		return element;
	}


	template<typename tchar>
	template<int Flags>
	XmlNode<tchar>* XmlDocument<tchar>::parseNode(tchar *&text)
	{
		// Parse proper node type
		switch (text[0])
		{
			// <...
		default:
			// Parse and append element node
			return parseElement<Flags>(text);

			// <?...
		case tchar('?'):
			++text;		// Skip ?
			if ((text[0] == tchar('x') || text[0] == tchar('X')) &&
				(text[1] == tchar('m') || text[1] == tchar('M')) &&
				(text[2] == tchar('l') || text[2] == tchar('L')) &&
				WhitespacePred::test(text[3]))
			{
				// '<?xml ' - xml declaration
				text += 4;		// Skip 'xml '
				return parseXmlDeclaration<Flags>(text);
			}
			else
			{
				// Parse PI
				return parsePI<Flags>(text);
			}

			// <!...
		case tchar('!'):

			// Parse proper subset of <! node
			switch (text[1])
			{
				// <!-
			case tchar('-'):
				if (text[2] == tchar('-'))
				{
					// '<!--' - xml comment
					text += 3;		// Skip '!--'
					return parseComment<Flags>(text);
				}
				break;

				// <![
			case tchar('['):
				if (text[2] == tchar('C') && text[3] == tchar('D') && text[4] == tchar('A') &&
					text[5] == tchar('T') && text[6] == tchar('A') && text[7] == tchar('['))
				{
					// '<![CDATA[' - cdata
					text += 8;		// Skip '![CDATA['
					return parseCdata<Flags>(text);
				}
				break;

				// <!D
			case tchar('D'):
				if (text[2] == tchar('O') && text[3] == tchar('C') && text[4] == tchar('T') &&
					text[5] == tchar('Y') && text[6] == tchar('P') && text[7] == tchar('E') &&
					WhitespacePred::test(text[8]))
				{
					// '<!DOCTYPE ' - doctype
					text += 9;		// skip '!DOCTYPE '
					return parseDoctype<Flags>(text);
				}
			}   // switch

			// Attempt to skip other, unrecognized node types starting with <!
			++text;		// Skip !
			while (*text != tchar('>'))
			{
				if (*text == 0)
					NightExcept(ERR_PARSE_XML, "unexpected end of data", text);
				++text;
			}
			++text;		// Skip '>'
			return 0;	// No node recognized
		}
	}


	template<typename tchar>
	template<int Flags>
	void XmlDocument<tchar>::parseNodeContents(tchar *&text, XmlNode<tchar> *node)
	{
		// For all children and text
		while (true)
		{
			// Skip whitespace between > and node contents
			tchar *contents_start = text;	// Store start of node contents before whitespace is skipped
			skip<WhitespacePred, Flags>(text);
			tchar next_char = *text;

			// After data nodes, instead of continuing the loop, control jumps here.
			// This is because zero termination inside parseAndAppendData() function
			// would wreak havoc with the above code.
			// Also, skipping whitespace after data nodes is unnecessary.
after_data_node:

			// Determine what comes next: node closing, child node, data node, or 0?
			switch (next_char)
			{
				// Node closing or child node
			case tchar('<'):
				if (text[1] == tchar('/'))
				{
					// Node closing
					text += 2;		// Skip '</'
					if (Flags & xml_parse_validate_closing_tags)
					{
						// Skip and validate closing tag name
						tchar *closing_name = text;
						skip<NodeNamePred, Flags>(text);
						if (!XmlInternal::compare(node->getName(), node->getNameSize(), closing_name, text - closing_name, true))
							NightExcept(ERR_PARSE_XML, "invalid closing tag name", text);
					}
					else
					{
						// No validation, just skip name
						skip<NodeNamePred, Flags>(text);
					}
					// Skip remaining whitespace after node name
					skip<WhitespacePred, Flags>(text);
					if (*text != tchar('>'))
						NightExcept(ERR_PARSE_XML, "expected >", text);
					++text;		// Skip '>'
					return;		// Node closed, finished parsing contents
				}
				else
				{
					// Child node
					++text;		// Skip '<'
					if (XmlNode<tchar> *child = parseNode<Flags>(text))
						node->appendNode(child);
				}
				break;

				// End of data - error
			case tchar('\0'):
				NightExcept(ERR_PARSE_XML, "unexpected end of data", text);

				// Data node
			default:
				next_char = parseAndAppendData<Flags>(node, text, contents_start);
				goto after_data_node;	// Bypass regular processing after data nodes
			}
		}
	}


	template<typename tchar>
	template<int Flags>
	void XmlDocument<tchar>::parseNodeAttributes(tchar *&text, XmlNode<tchar> *node)
	{
		// For all attributes
		while (AttributeNamePred::test(*text))
		{
			// Extract attribute name
			tchar *name = text;
			++text;		// Skip first character of attribute name
			skip<AttributeNamePred, Flags>(text);
			if (text == name)
				NightExcept(ERR_PARSE_XML, "expected attribute name", name);

			// Create new attribute
			XmlAttribute<tchar> *attribute = this->allocateAttribute();
			attribute->setName(name, text - name);
			node->appendAttribute(attribute);

			// Skip whitespace after attribute name
			skip<WhitespacePred, Flags>(text);

			// Skip =
			if (*text != tchar('='))
				NightExcept(ERR_PARSE_XML, "expected =", text);
			++text;

			// Add terminating zero after name
			if (!(Flags & xml_parse_no_string_terminators))
				attribute->getName()[attribute->getNameSize()] = 0;

			// Skip whitespace after =
			skip<WhitespacePred, Flags>(text);

			// Skip quote and remember if it was ' or "
			tchar quote = *text;
			if (quote != tchar('\'') && quote != tchar('"'))
				NightExcept(ERR_PARSE_XML, "expected ' or \"", text);
			++text;

			// Extract attribute value and expand char refs in it
			tchar *value = text, *end;
			const int AttFlags = Flags & ~xml_parse_normalize_whitespace;	// No whitespace normalization in attributes
			if (quote == tchar('\''))
				end = skipAndExpandCharacterRefs<AttributeValuePred<tchar('\'')>, AttributeValuePurePred<tchar('\'')>, AttFlags>(text);
			else
				end = skipAndExpandCharacterRefs<AttributeValuePred<tchar('"')>, AttributeValuePurePred<tchar('"')>, AttFlags>(text);

			// Set attribute value
			attribute->setValue(value, end - value);

			// Make sure that end quote is present
			if (*text != quote)
				NightExcept(ERR_PARSE_XML, "expected ' or \"", text);
			++text;		// Skip quote

			// Add terminating zero after value
			if (!(Flags & xml_parse_no_string_terminators))
				attribute->getValue()[attribute->getValueSize()] = 0;

			// Skip whitespace after attribute value
			skip<WhitespacePred, Flags>(text);
		}
	}


	///////////////////////////////////// Printing /////////////////////////////////////

	//! \cond XmlInternal
	namespace XmlInternal
	{

		///////////////////////////////////////////////////////////////////////////
		// XmlInternal character operations

		// Copy characters from given range to given output iterator
		template<typename OutIterator, typename tchar>
		inline OutIterator copyChars(const tchar *begin, const tchar *end, OutIterator out)
		{
			while (begin != end)
				*out++ = *begin++;
			return out;
		}

		// Copy characters from given range to given output iterator and expand
		// characters into references (&lt; &gt; &apos; &quot; &amp;)
		template<typename OutIterator, typename tchar>
		inline OutIterator copyAndExpandChars(const tchar *begin, const tchar *end, tchar noexpand, OutIterator out)
		{
			while (begin != end)
			{
				if (*begin == noexpand)
				{
					*out++ = *begin;    // No expansion, copy character
				}
				else
				{
					switch (*begin)
					{
					case tchar('<'):
						*out++ = tchar('&'); *out++ = tchar('l'); *out++ = tchar('t'); *out++ = tchar(';');
						break;
					case tchar('>'):
						*out++ = tchar('&'); *out++ = tchar('g'); *out++ = tchar('t'); *out++ = tchar(';');
						break;
					case tchar('\''):
						*out++ = tchar('&'); *out++ = tchar('a'); *out++ = tchar('p'); *out++ = tchar('o'); *out++ = tchar('s'); *out++ = tchar(';');
						break;
					case tchar('"'):
						*out++ = tchar('&'); *out++ = tchar('q'); *out++ = tchar('u'); *out++ = tchar('o'); *out++ = tchar('t'); *out++ = tchar(';');
						break;
					case tchar('&'):
						*out++ = tchar('&'); *out++ = tchar('a'); *out++ = tchar('m'); *out++ = tchar('p'); *out++ = tchar(';');
						break;
					default:
						*out++ = *begin;    // No expansion, copy character
					}
				}
				++begin;    // Step to next character
			}
			return out;
		}

		// Fill given output iterator with repetitions of the same character
		template<typename OutIterator, typename tchar>
		inline OutIterator fillChars(OutIterator out, int n, tchar ch)
		{
			for (int i = 0; i < n; ++i)
				*out++ = ch;
			return out;
		}

		// Find character
		template<typename tchar, tchar ch>
		inline bool findChar(const tchar *begin, const tchar *end)
		{
			while (begin != end)
				if (*begin++ == ch)
					return true;
			return false;
		}

		///////////////////////////////////////////////////////////////////////////
		// XmlInternal printing operations

		// Print node
		template<typename OutIterator, typename tchar>
		inline OutIterator printNode(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			// Print proper node type
			switch (node->getType())
			{

				// Document
			case XML_NODE_DOCUMENT:
				out = printChildren(out, node, flags, indent);
				break;

				// Element
			case XML_NODE_ELEMENT:
				out = printElementNode(out, node, flags, indent);
				break;

				// Data
			case XML_NODE_DATA:
				out = printDataNode(out, node, flags, indent);
				break;

				// CDATA
			case XML_NODE_CDATA:
				out = printCdataNode(out, node, flags, indent);
				break;

				// Declaration
			case XML_NODE_DECLARATION:
				out = printDeclarationNode(out, node, flags, indent);
				break;

				// Comment
			case XML_NODE_COMMENT:
				out = printCommentNode(out, node, flags, indent);
				break;

				// Doctype
			case XML_NODE_DOCTYPE:
				out = printDoctypeNode(out, node, flags, indent);
				break;

				// Pi
			case XML_NODE_PI:
				out = printPINode(out, node, flags, indent);
				break;

				// Unknown
			default:
				NightExcept(ERR_PARSE_XML, "unknown xml node type to print", 0);
				break;
			}

			// If indenting not disabled, add line break after node
			if (!(flags & xml_print_no_indenting))
				*out = tchar('\n'), ++out;

			// Return modified iterator
			return out;
		}

		// Print children of the node
		template<typename OutIterator, typename tchar>
		inline OutIterator printChildren(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			for (XmlNode<tchar> *child = node->getFirstNode(); child; child = child->getNextSibling())
				out = printNode(out, child, flags, indent);
			return out;
		}

		// Print attributes of the node
		template<typename OutIterator, typename tchar>
		inline OutIterator printAttributes(OutIterator out, const XmlNode<tchar> *node, int flags)
		{
			for (XmlAttribute<tchar> *attribute = node->getFirstAttribute(); attribute; attribute = attribute->getNextAttribute())
			{
				if (attribute->getName() && attribute->getValue())
				{
					// Print attribute name
					*out = tchar(' '), ++out;
					out = copyChars(attribute->getName(), attribute->getName() + attribute->getNameSize(), out);
					*out = tchar('='), ++out;
					// Print attribute value using appropriate quote type
					if (findChar<tchar, tchar('"')>(attribute->getValue(), attribute->getValue() + attribute->getValueSize()))
					{
						*out = tchar('\''), ++out;
						out = copyAndExpandChars(attribute->getValue(), attribute->getValue() + attribute->getValueSize(), tchar('"'), out);
						*out = tchar('\''), ++out;
					}
					else
					{
						*out = tchar('"'), ++out;
						out = copyAndExpandChars(attribute->getValue(), attribute->getValue() + attribute->getValueSize(), tchar('\''), out);
						*out = tchar('"'), ++out;
					}
				}
			}
			return out;
		}

		// Print data node
		template<typename OutIterator, typename tchar>
		inline OutIterator printDataNode(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			NightAssert(node->getType() == XML_NODE_DATA);
			if (!(flags & xml_print_no_indenting))
				out = fillChars(out, indent, tchar('\t'));
			out = copyAndExpandChars(node->getValue(), node->getValue() + node->getValueSize(), tchar(0), out);
			return out;
		}

		// Print data node
		template<typename OutIterator, typename tchar>
		inline OutIterator printCdataNode(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			NightAssert(node->getType() == XML_NODE_CDATA);
			if (!(flags & xml_print_no_indenting))
				out = fillChars(out, indent, tchar('\t'));
			*out = tchar('<'); ++out;
			*out = tchar('!'); ++out;
			*out = tchar('['); ++out;
			*out = tchar('C'); ++out;
			*out = tchar('D'); ++out;
			*out = tchar('A'); ++out;
			*out = tchar('T'); ++out;
			*out = tchar('A'); ++out;
			*out = tchar('['); ++out;
			out = copyChars(node->getValue(), node->getValue() + node->getValueSize(), out);
			*out = tchar(']'); ++out;
			*out = tchar(']'); ++out;
			*out = tchar('>'); ++out;
			return out;
		}

		// Print element node
		template<typename OutIterator, typename tchar>
		inline OutIterator printElementNode(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			NightAssert(node->getType() == XML_NODE_ELEMENT);

			// Print element name and attributes, if any
			if (!(flags & xml_print_no_indenting))
				out = fillChars(out, indent, tchar('\t'));
			*out = tchar('<'), ++out;
			out = copyChars(node->getName(), node->getName() + node->getNameSize(), out);
			out = printAttributes(out, node, flags);

			// If node is childless
			if (node->getValueSize() == 0 && !node->getFirstNode())
			{
				// Print childless node tag ending
				*out = tchar('/'), ++out;
				*out = tchar('>'), ++out;
			}
			else
			{
				// Print normal node tag ending
				*out = tchar('>'), ++out;

				// Test if node contains a single data node only (and no other nodes)
				XmlNode<tchar> *child = node->getFirstNode();
				if (!child)
				{
					// If node has no children, only print its value without indenting
					out = copyAndExpandChars(node->getValue(), node->getValue() + node->getValueSize(), tchar(0), out);
				}
				else if (child->getNextSibling() == 0 && child->getType() == XML_NODE_DATA)
				{
					// If node has a sole data child, only print its value without indenting
					out = copyAndExpandChars(child->getValue(), child->getValue() + child->getValueSize(), tchar(0), out);
				}
				else
				{
					// Print all children with full indenting
					if (!(flags & xml_print_no_indenting))
						*out = tchar('\n'), ++out;
					out = printChildren(out, node, flags, indent + 1);
					if (!(flags & xml_print_no_indenting))
						out = fillChars(out, indent, tchar('\t'));
				}

				// Print node end
				*out = tchar('<'), ++out;
				*out = tchar('/'), ++out;
				out = copyChars(node->getName(), node->getName() + node->getNameSize(), out);
				*out = tchar('>'), ++out;
			}
			return out;
		}

		// Print declaration node
		template<typename OutIterator, typename tchar>
		inline OutIterator printDeclarationNode(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			// Print declaration start
			if (!(flags & xml_print_no_indenting))
				out = fillChars(out, indent, tchar('\t'));
			*out = tchar('<'), ++out;
			*out = tchar('?'), ++out;
			*out = tchar('x'), ++out;
			*out = tchar('m'), ++out;
			*out = tchar('l'), ++out;

			// Print attributes
			out = printAttributes(out, node, flags);

			// Print declaration end
			*out = tchar('?'), ++out;
			*out = tchar('>'), ++out;

			return out;
		}

		// Print comment node
		template<typename OutIterator, typename tchar>
		inline OutIterator printCommentNode(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			NightAssert(node->getType() == XML_NODE_COMMENT);
			if (!(flags & xml_print_no_indenting))
				out = fillChars(out, indent, tchar('\t'));
			*out = tchar('<'), ++out;
			*out = tchar('!'), ++out;
			*out = tchar('-'), ++out;
			*out = tchar('-'), ++out;
			out = copyChars(node->getValue(), node->getValue() + node->getValueSize(), out);
			*out = tchar('-'), ++out;
			*out = tchar('-'), ++out;
			*out = tchar('>'), ++out;
			return out;
		}

		// Print doctype node
		template<typename OutIterator, typename tchar>
		inline OutIterator printDoctypeNode(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			NightAssert(node->getType() == XML_NODE_DOCTYPE);
			if (!(flags & xml_print_no_indenting))
				out = fillChars(out, indent, tchar('\t'));
			*out = tchar('<'), ++out;
			*out = tchar('!'), ++out;
			*out = tchar('D'), ++out;
			*out = tchar('O'), ++out;
			*out = tchar('C'), ++out;
			*out = tchar('T'), ++out;
			*out = tchar('Y'), ++out;
			*out = tchar('P'), ++out;
			*out = tchar('E'), ++out;
			*out = tchar(' '), ++out;
			out = copyChars(node->getValue(), node->getValue() + node->getValueSize(), out);
			*out = tchar('>'), ++out;
			return out;
		}

		// Print pi node
		template<typename OutIterator, typename tchar>
		inline OutIterator printPINode(OutIterator out, const XmlNode<tchar> *node, int flags, int indent)
		{
			NightAssert(node->getType() == XML_NODE_PI);
			if (!(flags & xml_print_no_indenting))
				out = fillChars(out, indent, tchar('\t'));
			*out = tchar('<'), ++out;
			*out = tchar('?'), ++out;
			out = copyChars(node->getName(), node->getName() + node->getNameSize(), out);
			*out = tchar(' '), ++out;
			out = copyChars(node->getValue(), node->getValue() + node->getValueSize(), out);
			*out = tchar('?'), ++out;
			*out = tchar('>'), ++out;
			return out;
		}

	}
	//! \endcond


	template<typename OutIterator, typename tchar>
	inline OutIterator XmlPrint(OutIterator out, const XmlNode<tchar> &node, int flags /*= 0*/)
	{
		return XmlInternal::printNode(out, &node, flags, 0);
	}

}


#endif
